package com.shure.surdes.common.query.condition;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.shure.surdes.common.query.Query;
import com.shure.surdes.common.query.annotation.IgnoreQuery;
import com.shure.surdes.common.query.annotation.Wrapper;
import com.shure.surdes.common.utils.CloneUtil;
import org.apache.commons.lang3.StringUtils;

import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

/**
 * @ClassName KCondition
 * @Description 查询条件封装的工具类
 * @Author shure
 * @Date 2023/11/24 14:20
 * @Version 1.0
 **/
public class Condition {
    public Condition() {
    }

    public static <T> LambdaQueryWrapper<T> getLambdaQueryWrapper(Query entity) {
        T obj = CloneUtil.clone(entity, entity.getClazz());
        Field[] fields = entity.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {

                String fieldName = field.getName();
                Method getMethod = null, setMethod = null;
                String setMethodName = "set" + captureName(fieldName);
                String getMethodName = "get" + captureName(fieldName);

                Method[] methods = obj.getClass().getMethods();
                for (Method me : methods) {
                    if (me.getName().equals(setMethodName)) {
                        setMethod = me;
                    }
                    if (me.getName().equals(getMethodName)) {
                        getMethod = me;
                    }
                }

                // 实体中没有的字段
                if (getMethod == null || setMethod == null) {
                    continue;
                }

                // 忽略的字段
                IgnoreQuery ignoreQuery = field.getAnnotation(IgnoreQuery.class);
                if (Objects.nonNull(ignoreQuery)) {
                    setMethod = obj.getClass().getDeclaredMethod("set" + captureName(fieldName), field.getType());
                    setMethod.invoke(obj, (Object) null);
                }

                //getMethod = obj.getClass().getDeclaredMethod("get" + captureName(fieldName));
                assert getMethod != null;
                Object value = getMethod.invoke(obj);
                if (value instanceof String val) {
                    if (StringUtils.isBlank(val)) {
                        //method = obj.getClass().getDeclaredMethod("set" + captureName(fieldName), field.getType());
                        assert setMethod != null;
                        setMethod.invoke(obj, (Object) null);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return new LambdaQueryWrapper<>(obj);
    }

    public static <T> LambdaQueryWrapper<T> getLambdaQueryWrapper1(Query entity) {
        T obj = CloneUtil.clone(entity, entity.getClazz());
        LambdaQueryWrapper<T> wrapper = new LambdaQueryWrapper<>();
        Field[] fields = entity.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                String fieldName = field.getName();
                String getMethodName = "get" + captureName(fieldName);

                Method getMethod = getDeclaredMethodWithParent(obj.getClass(), getMethodName);
                if (getMethod == null)
                    continue;

                Object value = getMethod.invoke(obj);
                if (Objects.isNull(value))
                    continue;

                Wrapper where = field.getAnnotation(Wrapper.class);
                if (Objects.nonNull(where)) {
                    SFunction func = getSFunction(obj.getClass(), fieldName);
                    switch (where.keyword()) {
                        case eq:
                            wrapper.eq(func, value);
                            break;
                        case ne:
                            wrapper.ne(func, value);
                            break;
                        case lt:
                            wrapper.lt(func, value);
                            break;
                        case le:
                            wrapper.le(func, value);
                            break;
                        case gt:
                            wrapper.gt(func, value);
                            break;
                        case ge:
                            wrapper.ge(func, value);
                            break;
                        case like:
                            wrapper.like(func, value);
                            break;
                        case notLike:
                            wrapper.notLike(func, value);
                            break;
                        case likeLeft:
                            wrapper.likeLeft(func, value);
                            break;
                        case likeRight:
                            wrapper.likeRight(func, value);
                            break;
                        case isNull:
                            wrapper.isNull(func);
                            break;
                        case isNotNull:
                            wrapper.isNotNull(func);
                            break;
                        case orderByAsc:
                            wrapper.orderByAsc(getSFunction(obj.getClass(), fieldName));
                            break;
                        case orderByDesc:
                            wrapper.orderByDesc(getSFunction(obj.getClass(), fieldName));
                            break;
                        case in:
                            List<String> inList = StrUtil.split((CharSequence) value, StrUtil.C_COMMA, true, true);
                            wrapper.in(func, inList);
                            break;
                        case notIn:
                            List<String> notInList = StrUtil.split((CharSequence) value, StrUtil.C_COMMA, true, true);
                            wrapper.notIn(func, notInList);
                            break;
                        default:
                            break;
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return wrapper;
    }

    /**
     * 将字符串的首字母转大写
     *
     * @param str 需要转换的字符串
     * @return
     */
    private static String captureName(String str) {
        // 进行字母的ascii编码前移，效率要高于截取字符串进行转换的操作
        char[] cs = str.toCharArray();
        cs[0] -= 32;
        return String.valueOf(cs);
    }


    private static final java.util.Map<String, SFunction> functionMap = new java.util.HashMap<>();

    private static SFunction getSFunction(Class<?> entityClass, String fieldName) throws NoSuchFieldException {
        if (functionMap.containsKey(entityClass.getName() + fieldName)) {
            return functionMap.get(entityClass.getName() + fieldName);
        }

        Field field = getDeclaredFieldWithParent(entityClass, fieldName);

        SFunction func = null;
        final MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType methodType = MethodType.methodType(field.getType(), entityClass);
        final CallSite site;
        String getFunName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        try {
            site = LambdaMetafactory.altMetafactory(lookup,
                    "invoke",
                    MethodType.methodType(SFunction.class),
                    methodType,
                    lookup.findVirtual(entityClass, getFunName, MethodType.methodType(field.getType())),
                    methodType, LambdaMetafactory.FLAG_SERIALIZABLE);
            func = (SFunction) site.getTarget().invokeExact();
            functionMap.put(entityClass.getName() + field, func);
            return func;
        } catch (Throwable e) {
            throw ExceptionUtils.mpe("This class %s is not have method %s ", entityClass.getName(), getFunName);
        }
    }

    private static Method getDeclaredMethodWithParent(Class targetClass, String methodName, Class<?>... parameteTypes) throws NoSuchMethodException {
        Boolean doneFlag = false;
        Class curClass = targetClass;
        Method declaredMethod = null;
        while (!doneFlag && curClass != Object.class) {
            try {
                declaredMethod = curClass.getDeclaredMethod(methodName, parameteTypes);
            } catch (NoSuchMethodException e) {
                doneFlag = false;
                curClass = curClass.getSuperclass();
                continue;
            }
            doneFlag = true;
        }
        return declaredMethod;

    }

    private static Field getDeclaredFieldWithParent(Class clazz, String fieldName) throws NoSuchFieldException {
        Field fi = null;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {//向上循环 遍历父类
            Field[] field = clazz.getDeclaredFields();
            for (Field f : field) {
                f.setAccessible(true);
                if (f.getName().equals(fieldName)) {
                    fi = f;
                    break;
                }
            }
        }
        return fi;
    }


}
